

module udp_tx #(
    parameter           SOURCE_PORT = 16'd5001      
)(
    input                       i_clk                           ,
    input                       i_rst                           ,
    input                       i_set_source_port_valid         ,
    input  [15: 0]              i_set_source_port               ,
    input                       s_axis_tvalid                   ,
    input  [7 : 0]              s_axis_tdata                    ,
    output                      s_axis_tready                   ,
    input                       s_axis_tlast                    ,
    input  [63: 0]              s_axis_tuser                    ,
    output                      m_axis_tvalid                   ,
    output [7 : 0]              m_axis_tdata                    ,
    input                       m_axis_tready                   ,
    output                      m_axis_tlast                    ,
    output [55: 0]              m_axis_tuser                      
);

    localparam  UDP_PKG_TYPE     = 8'd17                    ;

    logic   [15: 0]             set_source_port             ;
    logic   [7 : 0]             axis_tx_data                ;
    logic                       axis_tx_last                ;
    logic                       axis_tx_valid               ;
    logic                       axis_tx_ready               ;
    logic                       tx_handshake                ;
    logic   [15: 0]             lsm_cnt                     ;
    logic   [15: 0]             udp_tx_target_port          ;  
    logic   [15: 0]             udp_tx_pkg_len              ;
    logic                       fifo_rden                   ;
    logic   [7 : 0]             fifo_rddata                 ;
    logic                       fifo_almost_full            ;
    logic                       fifo_full                   ;
    logic                       fifo_empty                  ;
    logic                       fifo_empty_delay            ;
    logic   [7 : 0]             udp_axis_tx_data            ;
    logic   [55: 0]             udp_axis_tx_user            ;
    logic                       udp_axis_tx_last            ;
    logic                       udp_axis_tx_valid           ;

    // ila128_bit ila128_bit_check_tx(
    //     .clk    ( i_clk),
    //     .probe0 ( {s_axis_tvalid,s_axis_tdata,s_axis_tready,s_axis_tlast,s_axis_tuser[15:0],fifo_empty,fifo_rden,lsm_cnt,
    //     m_axis_tvalid,m_axis_tready,m_axis_tlast,udp_axis_tx_user[15:0]
    //     })

    // );

    assign s_axis_tready = axis_tx_ready;

    assign m_axis_tdata  = udp_axis_tx_data;
    assign m_axis_tuser  = udp_axis_tx_user;
    assign m_axis_tlast  = udp_axis_tx_last;
    assign m_axis_tvalid = udp_axis_tx_valid;
    
    always_ff @(posedge i_clk) begin
        if(i_rst) begin
            axis_tx_data  <= 0;
            axis_tx_last  <= 0;
            axis_tx_valid <= 0;
        end 
        else begin
            axis_tx_data  <= s_axis_tdata ;
            axis_tx_last  <= s_axis_tlast ;
            axis_tx_valid <= s_axis_tvalid;
        end
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            set_source_port  <= SOURCE_PORT;
        else if(i_set_source_port_valid)
            set_source_port  <= i_set_source_port;
        else 
            set_source_port  <= set_source_port;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            udp_tx_target_port  <= SOURCE_PORT;
        else if(s_axis_tvalid)
            udp_tx_target_port  <= s_axis_tuser[63:48];
        else 
            udp_tx_target_port  <= udp_tx_target_port;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            udp_tx_pkg_len <= 0;  
        else if(s_axis_tvalid) 
            udp_tx_pkg_len <= s_axis_tuser[15:0] + 8;
        else 
            udp_tx_pkg_len <= udp_tx_pkg_len;
    end 

 

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            tx_handshake  <= 0;
        else 
            tx_handshake  <= s_axis_tvalid & s_axis_tready;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            axis_tx_ready <= 0;
        else if(s_axis_tlast)
            axis_tx_ready <= 0;
        else if(fifo_empty & !udp_axis_tx_valid & m_axis_tready)
            axis_tx_ready <= 1;
        else 
            axis_tx_ready <= axis_tx_ready;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            lsm_cnt <= 0;
        else if(lsm_cnt == udp_tx_pkg_len - 1)
            lsm_cnt <= 0;
        else if(!fifo_empty & m_axis_tready)
            lsm_cnt <= lsm_cnt + 1;
        else 
            lsm_cnt <= lsm_cnt;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            fifo_rden <= 0;
        else if(lsm_cnt >= 7 && lsm_cnt < udp_tx_pkg_len - 1 )
            fifo_rden <= 1;
        else 
            fifo_rden <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst) 
            fifo_empty_delay <= 0;
        else 
            fifo_empty_delay <= fifo_empty;
    end

    sfifo_8x64 sfifo_8x64_inst1 (
        .clk                    ( i_clk                     ),
        .srst                   ( i_rst                     ),                
        .din                    ( axis_tx_data              ),
        .wr_en                  ( tx_handshake              ),
        .rd_en                  ( fifo_rden                 ),
        .dout                   ( fifo_rddata               ),
        .almost_full            ( fifo_almost_full          ),
        .full                   ( fifo_full                 ),
        .empty                  ( fifo_empty                ) 
    );

    always_ff @(posedge i_clk,posedge i_rst) begin
        if(i_rst) 
            udp_axis_tx_data <= 0;
        else case(lsm_cnt)
            0           :udp_axis_tx_data <= set_source_port[15: 8];
            1           :udp_axis_tx_data <= set_source_port[7 : 0];
            2           :udp_axis_tx_data <= udp_tx_target_port[15: 8];
            3           :udp_axis_tx_data <= udp_tx_target_port[7 : 0];
            4           :udp_axis_tx_data <= udp_tx_pkg_len[15: 8];//counter
            5           :udp_axis_tx_data <= udp_tx_pkg_len[7 : 0];//counter
            6           :udp_axis_tx_data <= 8'h00;//checksum
            7           :udp_axis_tx_data <= 8'h00;//checksum
            default     :udp_axis_tx_data <= fifo_rddata;
        endcase
    end
    
    always_ff @(posedge i_clk) begin
        if(i_rst)
            udp_axis_tx_user <= 0;
        else if(!fifo_empty & m_axis_tready & fifo_empty_delay)
            udp_axis_tx_user <= {s_axis_tuser[47:16], UDP_PKG_TYPE, udp_tx_pkg_len};
        else 
            udp_axis_tx_user <= udp_axis_tx_user;
    end 

    always_ff @(posedge i_clk) begin
        if(i_rst)
            udp_axis_tx_last <= 0;
        else if(lsm_cnt == udp_tx_pkg_len - 1)
            udp_axis_tx_last <= 1;
        else 
            udp_axis_tx_last <= 0;
    end

    always_ff @(posedge i_clk) begin
        if(i_rst)
            udp_axis_tx_valid <= 0;
        else if(udp_axis_tx_last & udp_axis_tx_valid)
            udp_axis_tx_valid <= 0;
        else if(!fifo_empty & m_axis_tready)
            udp_axis_tx_valid <= 1;
        else 
            udp_axis_tx_valid <= udp_axis_tx_valid;
    end 

endmodule
